import Mexp from 'math-expression-evaluator'
import { COLOR } from '../color'

import type { StyleTypes } from '../../types/styleTypes'
import type { StyleParsed } from '../../types/styleParsed'

export {
  MAIN,
  PRESET,
  RESULT,
  parse,
  parseStatus,
  parseIndent,
  parseControl,
  parseBoxes,
  parseFlexes,
}

const evaluator = new Mexp()

/** 解析时全局缓存的字典 */
const MAIN: {
  UNIT: {
    /** 总建筑高度 */
    H: number
    /** 层高 */
    FH: number
    /** 段高 */
    SH: number
    [k: string]: number
  }
} = { UNIT: { H: 0, FH: 0, SH: 0 } }
const PRESET: {
  UNIT: { [k: string]: number }
  COLOR: { [k: string]: string | string[] }
} = {
  UNIT: {},
  COLOR: {},
}

/** 全局缓存的解析结果，以便拆分函数 */
const RESULT: StyleParsed.Result = {
  colors: [],
  floorCount: 0,
  classified: {},
}

/** 解析带单位的公式。 */
function parse(nbs: StyleTypes.NBS | undefined, defaultValue = 0): number {
  let result = defaultValue
  if (typeof nbs === 'string') {
    // 计算单位值
    nbs = replaceUnit(nbs, MAIN.UNIT)
    nbs = replaceUnit(nbs, PRESET.UNIT)

    try {
      result = evaluator.eval(nbs)
      // n = eval(ns) 或使用子线程沙盒
    } catch (error) {
      console.error(`'${nbs}' parsing error`, error)
    }
  } else if (typeof nbs === 'boolean') {
    result = nbs ? 1 : 0
  } else if (nbs) {
    result = nbs
  }

  return result
}

/** 替换单位值，如单位前数字省略则默认为1 */
function replaceUnit(input: string, units?: StyleParsed.UnitType) {
  if (units) {
    // 优先解析长字符的变量，防止"变量A"先于"变量AB"解析导致后者错误
    const keys = Object.keys(units).sort((a, b) => b.length - a.length)
    keys.forEach((k) => {
      input = input.replace(new RegExp(`(\\d+(\\.\\d+)?)?${k}`, 'g'), (m) => {
        const u = Number(units[k])
        const n = Number(m.replace(k, '')) || 1
        return (n * u).toString()
      })
    })
  }
  return input
}

function parseIndent(params?: StyleTypes.IndentType): StyleParsed.IndentType | undefined {
  return params
    ? {
        start: parse(params.start),
        end: parse(params.end),
        central: parse(params.central),
        asRatio: parse(params.asRatio),
        reverse: parse(params.reverse),
      }
    : undefined
}

function formatColor(
  colorMap: string[],
  color?: string | string[]
): { index: number; glass: StyleParsed.Bool }[] {
  const a = Array.isArray(color) ? color : [color]
  return a.map((x) => {
    let c = x ? x.trim() : COLOR.CONCRETE
    if (c === 'G') c = COLOR.GLASS
    const cv = c.replace(/ *G$/, '')

    // 颜色先加入 colorMap 再从中索引
    let index = colorMap.indexOf(cv)
    if (index < 0) {
      index = colorMap.length
      colorMap.push(cv)
    }
    return { index, glass: /G$/.test(c) ? 1 : 0 }
  })
}

/** 解析包含 StyleTypes.status 的参数 */
function parseStatus<MORE>(status: StyleTypes.Status, data: MORE): StyleParsed.Status & MORE {
  let c = status.color
  // c 可能为预设的颜色名称，先进行解析
  if (typeof c === 'string') c = PRESET.COLOR?.[c] || c
  return Object.assign(
    {
      trans: status.trans
        ? status.trans.map((transform) => {
            if ('rotateX' in transform) {
              return { rotateX: parse(transform.rotateX) }
            } else if ('rotateY' in transform) {
              return { rotateY: parse(transform.rotateY) }
            } else if ('rotateZ' in transform) {
              return { rotateZ: parse(transform.rotateZ) }
            } else {
              return {
                moveX: parse(transform.moveX),
                moveY: parse(transform.moveY),
                moveZ: parse(transform.moveZ),
              }
            }
          })
        : [],
      color: formatColor(RESULT.colors, c),
    },
    data
  )
}

function parseBoxes(params?: StyleTypes.Box[]): StyleParsed.Box[] {
  return params
    ? params.map((b) =>
        parseStatus(b, {
          widthX: parse(b.widthX),
          depthY: parse(b.depthY),
          heightZ: parse(b.heightZ),
        })
      )
    : []
}

function parseFlexes(params?: StyleTypes.FlexVertical[]): StyleParsed.FlexVertical[] {
  return params
    ? params.map((x) => {
        return parseStatus(x, {
          unitHeightZ: parse(x.unitHeightZ),
          totalHeightZ: parse(x.totalHeightZ),
          depthY: parse(x.depthY),
          widthX: parse(x.widthX),
          dash: parseControl(x.dash),
          shrink: parseIndent(x.shrink),
          seg: parse(x.seg),
        })
      })
    : []
}

// function parseVerticalBoxes(
//   boxes?: (StyleTypes.box | StyleTypes.flexVertical)[]
// ): (StyleParsed.box | StyleParsed.flexVertical)[] {
//   return boxes
//     ? boxes.map((x) => {
//         if ('widthX' in x) {
//           return parseBox(x)
//         } else {
//           return parseStatus(x, {
//             unitHeight: parse(x.unitHeight),
//             totalHeight: parse(x.totalHeight),
//             flexDepth: parse(x.flexDepth),
//             flexwidth: parse(x.flexwidth),
//             dash: parseControl(x.dash)!,
//           })
//         }
//       })
//     : []
// }

function parseControl(
  params?: StyleTypes.IndexController
): StyleParsed.IndexController | undefined {
  return params
    ? {
        total: parse(params.total),
        indent: parseIndent(params.indent),
        filter: params.filter?.map(parse),
        chance: parse(params.chance),
      }
    : undefined
}
